package pl.dsp.dsp1.processing;

import java.util.HashMap;
import java.util.Random;

public class GeneratorFactory {
	public enum Source {
		SINE, SINE1, SINE2, GAUSSIAN_NOISE, UNIFORM_NOISE, RECTANGLE, RECTANGLE2, TRIANGLE, UNITARY, IMPULSE, IMPULSE_NOISE, COMPLEX_SINE, S3
	}

	static public Generator selectGenerator(Source source, double kw) {
		kw = kw == 0.0 ? 0.00000001 : kw;
		kw = kw == 1.0 ? 0.99999999 : kw;
		final double fixedKw = kw;

		HashMap<Source, Generator> generators = new HashMap<Source, Generator>();
		generators.put(Source.SINE, new Generator() {
			@Override
			public SignalValue at(double x) {
				return new SimpleValue(Math.sin(x * 2 * Math.PI));
			}

			@Override
			public boolean isPeriodical() {
				return true;
			}
		});
		generators.put(Source.SINE1, new Generator() {
			@Override
			public SignalValue at(double x) {
				double sine = Math.sin(x * 2 * Math.PI);
				return new SimpleValue((sine + Math.abs(sine)) / 2);
			}

			@Override
			public boolean isPeriodical() {
				return true;
			}
		});
		generators.put(Source.SINE2, new Generator() {
			@Override
			public SignalValue at(double x) {
				return new SimpleValue(Math.abs(Math.sin(x * Math.PI)));
			}

			@Override
			public boolean isPeriodical() {
				return true;
			}
		});
		generators.put(Source.GAUSSIAN_NOISE, new Generator() {
			@Override
			public SignalValue at(double x) {
				Random g = new Random();
				return new SimpleValue(g.nextGaussian());
			}

			@Override
			public boolean isPeriodical() {
				return false;
			}
		});
		generators.put(Source.UNIFORM_NOISE, new Generator() {
			@Override
			public SignalValue at(double x) {
				Random g = new Random();
				return new SimpleValue(g.nextFloat());
			}

			@Override
			public boolean isPeriodical() {
				return false;
			}
		});
		generators.put(Source.RECTANGLE, new Generator() {
			@Override
			public SignalValue at(double x) {
				double value = (x - Math.floor(x) <= fixedKw) ? 1 : 0;
				return new SimpleValue(value);
			}

			@Override
			public boolean isPeriodical() {
				return true;
			}
		});
		generators.put(Source.RECTANGLE2, new Generator() {
			@Override
			public SignalValue at(double x) {
				double value = (x - Math.floor(x) <= fixedKw) ? 1 : -1;
				return new SimpleValue(value);
			}

			@Override
			public boolean isPeriodical() {
				return true;
			}
		});
		generators.put(Source.TRIANGLE, new Generator() {
			@Override
			public SignalValue at(double x) {
				double position = x - Math.floor(x);
				double value = (position <= fixedKw) ? position / fixedKw
						: (1.0 - position) / (1.0 - fixedKw);
				return new SimpleValue(value);
			}

			@Override
			public boolean isPeriodical() {
				return true;
			}
		});
		generators.put(Source.UNITARY, new Generator() {
			@Override
			public SignalValue at(double x) {
				double value = x == 0 ? 0.5 : Math.signum(x);
				return new SimpleValue(value);
			}

			@Override
			public boolean isPeriodical() {
				return false;
			}
		});
		generators.put(Source.IMPULSE, new Generator() {
			@Override
			public SignalValue at(double x) {
				double value = x == 0 ? 1.0 : 0.0;
				return new SimpleValue(value);
			}

			@Override
			public boolean isPeriodical() {
				return false;
			}
		});
		generators.put(Source.IMPULSE_NOISE, new Generator() {
			@Override
			public SignalValue at(double x) {
				Random g = new Random();
				double value = g.nextDouble() < fixedKw ? 1.0 : 0.0;
				return new SimpleValue(value);
			}

			@Override
			public boolean isPeriodical() {
				return false;
			}
		});
		generators.put(Source.COMPLEX_SINE, new Generator() {
			@Override
			public SignalValue at(double x) {
				return ComplexValue.euler(x * 2 * Math.PI);
			}

			@Override
			public boolean isPeriodical() {
				return true;
			}
		});
		generators.put(Source.S3, new Generator() {
			@Override
			public SignalValue at(double x) {
				return new SimpleValue(5 * Math.sin(Math.PI * x)
						+ Math.sin(8 * Math.PI * x));
			}

			@Override
			public boolean isPeriodical() {
				return false;
			}
		});
		return generators.get(source);
	}
}
